# -*- coding:UTF-8 -*-
'''
Copyright 2009 http://code.google.com/p/mygaepy/. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
  * Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above
    copyright notice, this list of conditions and the following
    disclaimer in the documentation and/or other materials provided
    with the distribution.
  * Neither the name of http://code.google.com/p/mygaepy/ nor the names of its
    contributors may be used to endorse or promote products derived
    from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
import logging
import pickle

from google.appengine.api import memcache
from google.appengine.runtime import apiproxy_errors
from google.appengine.ext import db

from api.models import *

def ip2num(ip):
    w,x,y,z=tuple([int(x) for x in ip.split(".")])
    num=16777216*w + 65536*x + 256*y + z
    return num

def num2ip(num):
    ip=[]
    ip.append(num/16777216)
    num%=16777216
    ip.append(num/65536)
    num%=65536
    ip.append(num/256)
    num%=256
    ip.append(num)
    ip=".".join([str(s) for s in ip])
    return ip
    
def getLocIdFromBlocks(data,num):
    loc=None
    blocks=pickle.loads(data)
    i=0
    j=len(blocks)-1
    while i<=j:
        mid=(i+j)/2
        if num>=blocks[mid][0] and num<=blocks[mid][1]:
            loc=blocks[mid][2]
            break
        if num<blocks[mid][0]:
            j=mid-1
        else:
            i=mid+1
    """
    for b in blocks:
        if num>=b[0] and num<=b[1]:
            loc=b[2]
    """
    return loc

def getLocationFromLocId(num):
    #query=db.GqlQuery ("SELECT * FROM GeoIPLocation WHERE begloc<=:1 ORDER BY begloc desc,endloc LIMIT 1",loc)
    #query=db.GqlQuery ("SELECT * FROM GeoIPLocation WHERE begloc>=:1 ORDER BY begloc,endloc LIMIT 1",loc)
    query=db.GqlQuery ("SELECT * FROM GeoIPLocation WHERE id=:1",(num-1)/1000+1)
    for l in query:
        data=l.data
    locs=pickle.loads(data)
    logging.info("%d,"%len(locs))
    i=0
    j=len(locs)-1
    loc=None
    while i<=j:
        #logging.info("%d,%d"%(i,j))
        mid=(i+j)/2
        if num==locs[mid]["loc"]:
            loc=locs[mid]
            break
        if num<locs[mid]["loc"]:
            j=mid-1
        else:
            i=mid+1
    return loc
    
def getLocationByIP(ip):
    location=None
    try:
        location=memcache.get("location_%s"%ip)
    except apiproxy_errors.OverQuotaError, message:
        logging.error(message)
    if location is not None:
        return location
    num=ip2num(ip)
    block=None
    query=db.GqlQuery ("SELECT * FROM GeoIPBlocks WHERE s<=:1 ORDER BY s desc LIMIT 1",num)
    for b in query:
        block=b

    if block is None:
        logging.debug("No block for ip %s (num %d)"%(ip,num))
        return None
    if block.s>num or block.e<num:
        logging.debug("block error for ip %s (num %d)(start,end)=(%s,%s)"%(ip,num))
        return None
    loc=getLocIdFromBlocks(block.data,num)
    if loc is None:
        logging.debug("No Locid for ip %s (num %d)"%(ip,num))
        return None
    logging.debug("Locid=%d"%loc)
    location=getLocationFromLocId(loc)
    if location:
        try:
            r=memcache.set("location_%s"%ip,location)
            if not r:
                logging.error("set location of %s failed.")
        except apiproxy_errors.OverQuotaError, message:
             logging.error(message)
    return location

def getCZipInfo(num,data):
    ips=pickle.loads(data)
    info=None
    i=0
    j=len(ips)-1
    while i<j:
        if ips[j][0]<=num:
            info=ips[j]
            break
        mid=(i+j)/2
        if num>=ips[mid][0]:
            i=mid
            if num<ips[mid+1][0]:
                info=ips[mid]
                break                
        else:
            j=mid-1       
    """
    for i in range(i,len(ips)):
        info=ips[i]
        if ips[i][0]>num:
            return ips[i-1]
    """
    return info

def getCZinfo(ip):
    num=ip2num(ip)
    logging.info("czip %d"%num)
    ver=None
    try:
        ver=memcache.get("CZipVer")
    except apiproxy_errors.OverQuotaError, message:
        logging.error(message)
    if ver is None:
        query = db.GqlQuery ("SELECT * FROM CZip ORDER BY begip desc LIMIT 1")
        for v in query:
            info=getCZipInfo(0xffffff00L,v.data)
            ver=info[1]
            try:
                r=memcache.set("CZipVer",ver)
                if not r:
                    logging.error("save CZip version error")
            except apiproxy_errors.OverQuotaError, message:
                logging.error(message)

    info=None
    query=db.GqlQuery ("SELECT * FROM CZip WHERE begip <=:1 ORDER BY begip desc LIMIT 1",num)
    for i in query:
        info=getCZipInfo(num,i.data)
    if info is None:
        info="没有找到此IP数据\n "+ver
    else:
        info=info[1]
    return info
